package com.yangfan.thread.thread_wangwenjun04;

import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.Optional;

/**
 * @description: 自定义 Lock 实现
 * @author: yangfan
 * @date: Created in 2020/1/27 0027 17:18
 * @modified By:
 *
 */
public class BooleanLock implements Lock {

    //initValue为true表示已获取锁定。
    //initValue为false表示锁已释放（其他线程可以获取此锁。）
    private boolean initValue;

    //锁定的线程会加到这里，主要用来统计被 锁住的线程 与 线程数量
    private Collection<Thread> blockedThreadCollection = new ArrayList<>();

    //用来记录加锁的线程
    private Thread currentThread;

    public BooleanLock() {
        this.initValue = false;
    }

    @Override
    public synchronized void lock() throws InterruptedException {

        //进行判断这个锁是否被人持有了，如果别人持有了则进行等待。
        //当有线程释放锁的时候则会被唤醒，并重新判断 initValue 是否被人持有
        while (initValue) {
            blockedThreadCollection.add(Thread.currentThread());
            this.wait();
        }

        //如果没有线程持有则把 自己从 等待线程的集合中删除，并把锁状态改成持有，并记录锁的持有者
        blockedThreadCollection.remove(Thread.currentThread());
        this.initValue = true;
        this.currentThread = Thread.currentThread();

    }

    /**
     * 尝试 mills毫秒去获取锁 如果 mills多秒内无法获取则停止去获取
     * @param mills
     * @throws InterruptedException
     * @throws TimeOutException
     */
    @Override
    public synchronized void lock(long mills) throws InterruptedException, TimeOutException {

        if (mills <= 0)
            lock();

        long hasRemaining = mills;//尝试获取锁的时间

        //结束时间
        long endTime = System.currentTimeMillis() + mills;

        while (initValue) {//initValue为true表示已获取锁定。

            if (hasRemaining <= 0)//mills = 10  hasRemaining = 10
                throw new TimeOutException("Time out");

            //把当前线程加入线程集合中，并等待指定的时间
            blockedThreadCollection.add(Thread.currentThread());
            this.wait(mills);//然后进行等待

            hasRemaining = endTime - System.currentTimeMillis();

        }

        this.initValue = true;
        this.currentThread = Thread.currentThread();

    }

    @Override
    public synchronized void unlock() {

        //为了防止 其他线程恶意 释放锁 ，所以判断 调用释放锁的想成 与 加锁的线程是否是一个线程
        if (Thread.currentThread() == currentThread) {

            //释放锁，并唤醒所有等待线程
            this.initValue = false;
            Optional.of(Thread.currentThread().getName() + " release the lock monitor.")
                    .ifPresent(System.out::println);
            this.notifyAll();

        }
    }

    @Override
    public Collection<Thread> getBlockedThread() {
        return Collections.unmodifiableCollection(blockedThreadCollection);
    }

    @Override
    public int getBlockedSize() {
        return blockedThreadCollection.size();
    }
}
